package com.global.variable.common.web.utils;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.security.Key;
import java.security.MessageDigest;

/**
 * Created by xifan on 2/13/17.
 */

public class AES {
//-----類別常數-----
    /**
     * 預設的Initialization Vector，為16 Bits的0
     */
    private static final IvParameterSpec DEFAULT_IV = new IvParameterSpec(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
    /**
     * 加密演算法使用AES
     */
    private static final String ALGORITHM = "AES";
    /**
     * AES使用CBC模式與PKCS5Padding
     */
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    //密钥
    public static final String AES_KEY = "git.cqhilink.com";
    private static Logger logger = LoggerFactory.getLogger(AES.class);
//-----物件變數-----
    /**
     * 取得AES加解密的密鑰
     */
    private Key key;
    /**
     * AES CBC模式使用的Initialization Vector
     */
    private IvParameterSpec iv;
    /**
     * Cipher 物件
     */
    private Cipher cipher;

//-----建構子-----

    /**
     * 建構子，使用128 Bits的AES密鑰(計算任意長度密鑰的MD5)和預設IV
     *
     * @param key 傳入任意長度的AES密鑰
     */
    public AES(final String key) {
        this(key, 128);
    }

    /**
     * 建構子，使用128 Bits或是256 Bits的AES密鑰(計算任意長度密鑰的MD5或是SHA256)和預設IV
     *
     * @param key 傳入任意長度的AES密鑰
     * @param bit 傳入AES密鑰長度，數值可以是128、256 (Bits)
     */
    public AES(final String key, final int bit) {
        this(key, bit, null);
    }

    /**
     * 建構子，使用128 Bits或是256 Bits的AES密鑰(計算任意長度密鑰的MD5或是SHA256)，用MD5計算IV值
     *
     * @param key 傳入任意長度的AES密鑰
     * @param bit 傳入AES密鑰長度，數值可以是128、256 (Bits)
     * @param iv  傳入任意長度的IV字串
     */
    public AES(final String key, final int bit, final String iv) {
        if (bit == 256) {
            this.key = new SecretKeySpec(getHash("SHA-256", key), ALGORITHM);
        } else {
            this.key = new SecretKeySpec(getHash("MD5", key), ALGORITHM);
        }
        if (iv != null) {
            this.iv = new IvParameterSpec(getHash("MD5", iv));
        } else {
            this.iv = DEFAULT_IV;
        }

        init();
    }

//-----物件方法-----

    /**
     * 取得字串的雜湊值
     *
     * @param algorithm 傳入雜驟演算法
     * @param text      傳入要雜湊的字串
     * @return 傳回雜湊後資料內容
     */
    private static byte[] getHash(final String algorithm, final String text) {
        try {
            return getHash(algorithm, text.getBytes("UTF-8"));
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 取得資料的雜湊值
     *
     * @param algorithm 傳入雜驟演算法
     * @param data      傳入要雜湊的資料
     * @return 傳回雜湊後資料內容
     */
    private static byte[] getHash(final String algorithm, final byte[] data) {
        try {
            final MessageDigest digest = MessageDigest.getInstance(algorithm);
            digest.update(data);
            return digest.digest();
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 初始化
     */
    private void init() {
        try {
            cipher = Cipher.getInstance(TRANSFORMATION);
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 加密文字
     *
     * @param str 傳入要加密的文字
     * @return 傳回加密後的文字
     */
    public String encrypt(final String str) {
        try {
            return encrypt(str.getBytes("UTF-8"));
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 加密資料
     *
     * @param data 傳入要加密的資料
     * @return 傳回加密後的資料
     */
    public String encrypt(final byte[] data) {
        try {
            cipher.init(Cipher.ENCRYPT_MODE, key, iv);
            final byte[] encryptData = cipher.doFinal(data);
            //return Base64.encodeToString(encryptData, Base64.DEFAULT);
            return new BASE64Encoder().encode(encryptData);
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 解密文字
     *
     * @param str 傳入要解密的文字
     * @return 傳回解密後的文字
     */
    public JSONObject decrypt(final String str) throws Exception {
        logger.info("待解密的内容:"+ str);
        try {
            //return decrypt(Base64.decode(str, Base64.DEFAULT));
            String content = decrypt(new BASE64Decoder().decodeBuffer(str));
            logger.info("解密后的内容:"+content);
            return JSON.parseObject(content);
        } catch (final Exception ex) {
            logger.error("错误:"+ex.getMessage());
            return null;
        }
    }

    /**
     * 解密文字
     *
     * @param data 傳入要解密的資料
     * @return 傳回解密後的文字
     */
    public String decrypt(final byte[] data) {
        try {
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            final byte[] decryptData = cipher.doFinal(data);
            return new String(decryptData, "UTF-8");
        } catch (final Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    //测试方法
    public static void main(String[] args) throws Exception{
        JSONObject obj = new JSONObject();
        obj.put("targetId",0);
        //obj.put("timestamp",System.currentTimeMillis());
        //String key = encrypt(AES_MODE, JSON.toJSONString(obj));

        AES aes = new AES("git.cqhilink.com");
        String key = aes.encrypt(JSON.toJSONString(obj)).replace("+","%2B").replace("+"," ");
        obj.put("paramObj",key);
        /*
        System.out.println("加密之后："+key);
        System.out.println("解密之后："+ aes.decrypt(key));*/
        JSONObject result = JSON.parseObject(sendPost("http://192.168.0.15:8090/up-web/facade/ar/getById.htm",obj));
        System.out.println(  aes.decrypt(key.replace("%2B","+").replace(" ","+"))  );
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param obj
     *            请求参数，json字符串格式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, JSONObject obj) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("token","1111");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(obj.toJSONString());
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(Exception ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * MD5加密 ，生成32位编码
     * @param inputStr
     * @return
     */
    public static String encode32(String inputStr)
    {
        if(inputStr==null){
            return null;
        }
        MessageDigest md5 = null;
        try
        {
            md5 = MessageDigest.getInstance("MD5");
        }
        catch (Exception e)
        {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        char[] charArray = inputStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];


        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];


        byte[] md5Bytes = md5.digest(byteArray);


        StringBuffer hexValue = new StringBuffer();


        for (int i = 0; i < md5Bytes.length; i++)
        {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
}